/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include "ScanMethodPromisc.h"

//--------------------------------------------------------------------

ScanMethodPromisc::ScanMethodPromisc(void) : ScanMethod()
{
	myType = __wmtPromisc;
}

ScanMethodPromisc::~ScanMethodPromisc(void)
{
}

//--------------------------------------------------------------------

int ScanMethodPromisc::InternalInit(void)
{
	if(workPhase!=__wptDiscovery) return MSR_ERR_PHASE;

	return MSR_ERR_OK;
}

//--------------------------------------------------------------------

int ScanMethodPromisc::InternalExecute(void)
{
	ScanPacket* pack;
	ScanPair sp;
	WORD oldPort;
	DWORD gwIP;

	if(addrList==NULL) return MSR_ERR_PARAMETER;

	gwIP = winPcap->GetGatewayIP();

	oldPort = 0xFFFF;

	while(GetNextPair(&sp, __sptIpAndPromisc) && !stopStopStop){
		if(sp.port!=oldPort){
			while(PacketsUsed()>0){
				CheckResponses();
				CheckDeaths();
				SleepEx(1, TRUE);
			}
		}

		oldPort = sp.port;
		activePromiscMode = sp.port;

		if(sp.addr!=NULL){
			if(!sp.addr->addrIP.IsEmpty()){
				if(sp.addr->gwMAC && gwIP!=sp.addr->addrIP.GetD()){
					// outside my network segment, ignore it
				}
				else{
					if(workPhase==__wptDiscovery && sp.addr->alive){
						// it's the discovery phase and a previous discovery method has found it already
						// so skip this one
					}
					else{
						pack = FindFirstUnusedPacket();
						while(pack==NULL){
							SleepEx(waitTime, TRUE);
							CheckResponses();
							CheckDeaths();
							pack = FindFirstUnusedPacket();
						}

						if(pack!=NULL){
							pack->Initialize(true);
							pack->sTime       = GetTickCount();
							pack->addr        = sp.addr->addrIP.GetD();
							pack->port        = sp.port;
							pack->methodType  = myType;
							pack->fullAddress = sp.addr;
							pack->machineInfo = FindMachineByIp(pack->addr);

							if(!SendArpRequest(sp.addr, sp.port, pack))
								pack->Initialize(false);

							if((packSent % notifTcp) == 0 && packSent>0){
								if(hWnd!=NULL)
									PostMessage(hWnd, CHS_MSG_PACKETS_SENT, 0, (LPARAM)packSent);
							}
						}
					}
				}
			}
		}
	}

	// wait until the end
	while(PacketsUsed()>0){
		CheckResponses();
		CheckDeaths();
		SleepEx(1, TRUE);
	}

	addrIdx = 0;
	while(GetNextPair(&sp, __sptIpOnly)){
		if(sp.addr!=NULL){
			if((sp.addr->promiscState & 0x1F)!=0){
				// the target host has responded to more then mCast1. All should reply to mCast1.
				sp.addr->alive = true;
			}
		}
	}

	return MSR_ERR_OK;
}

//---------------------------------------------------------------------------

bool ScanMethodPromisc::SendArpRequest(MFullAddress* addrIn, WORD portIn, ScanPacket* packIn)
{
	BYTE srcMAC[6];
	BYTE srcIP[4];
	BYTE dstIP[4];
	in_addr inAddr;

	if(addrIn==NULL){
		// nothing to send
		return false;
	}

	winPcap->FillSrcMAC(&srcMAC[0]);

	winPcap->FillSrcIp(&srcIP[0]);

	inAddr.S_un.S_addr = addrIn->addrIP.GetD();
	dstIP[0] = inAddr.S_un.S_un_b.s_b1;
	dstIP[1] = inAddr.S_un.S_un_b.s_b2;
	dstIP[2] = inAddr.S_un.S_un_b.s_b3;
	dstIP[3] = inAddr.S_un.S_un_b.s_b4;

	if(!packetBuilderPromisc->BuildPacketPromisc((PromiscPacketType)portIn, srcMAC, srcIP, dstIP))
		return false;

	return SendPacket(packetBuilderPromisc->GetPacket(), (int)packetBuilderPromisc->GetPacketSize());
}

//---------------------------------------------------------------------------

BYTE ScanMethodPromisc::PromiscModeFromMode(void)
{
	PromiscPacketType ppt;
	BYTE res;

	ppt = (PromiscPacketType)activePromiscMode;
	switch(ppt){
		case __pptBcast47 :
			res = 0x01;
			break;
		case __pptBcast16 :
			res = 0x02;
			break;
		case __pptBcast8 :
			res = 0x04;
			break;
		case __pptGroup :
			res = 0x08;
			break;
		case __pptMcast0 :
			res = 0x10;
			break;
		case __pptMcast1 :
			res = 0x20;
			break;

		default:
			res = 0x00;
			break;
	}

	return res;
}

void ScanMethodPromisc::ProcessArpReply(BYTE* dataIn, int sizeIn)
{
	in_addr inAddr;
	ScanPacket* pack;
	MFullAddress* addr;

	if(sizeIn<42){
		// nasol, pachet prea mic
		return;
	}

	inAddr.S_un.S_un_b.s_b1 = dataIn[14];
	inAddr.S_un.S_un_b.s_b2 = dataIn[15];
	inAddr.S_un.S_un_b.s_b3 = dataIn[16];
	inAddr.S_un.S_un_b.s_b4 = dataIn[17];

	pack = FindPacket(inAddr.S_un.S_addr, activePromiscMode, myType);
	if(pack==NULL) return;

	addr = pack->fullAddress;
	if(addr!=NULL){
		addr->promiscState |= PromiscModeFromMode();
//		addr->alive = true;
		// it will be marked alive at the end of the method !
	}
	pack->Initialize(false);
}

//---------------------------------------------------------------------------
